use aes::Aes256;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Ecb}; 
use base64;
use std::error::Error;

use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm`
use aes_gcm::aead::{Aead, NewAead,Payload};
/** 加密 **/ 
pub fn encrypt(data: String,keystr: String,ivstr: String) ->  Result<String, Box<dyn Error>>{ 
    type Aes256Ecb = Ecb<Aes256, Pkcs7>; 
    let key =&keystr.as_bytes(); 
    let iv = &ivstr.as_bytes(); 
    let plaintext = data.as_bytes(); 
    let cipher = Aes256Ecb::new_from_slices(&key, &iv)?; 
    let mut buffer = [0u8; 32]; 
    // copy message to the buffer 
    let pos = plaintext.len(); 
    buffer[..pos].copy_from_slice(plaintext); 
    let ciphertext = cipher.encrypt(&mut buffer, pos)?; 
    let enc=base64::encode(ciphertext); 
    Ok(enc) 
}

/** 解密 **/ 
pub fn decrypt(data: String,keystr: String,ivstr: String) ->  Result<String, Box<dyn Error>> { 
    type Aes256Ecb = Ecb<Aes256, Pkcs7>; 
    let key =&keystr.as_bytes(); 
    let iv = &ivstr.as_bytes();
    let cipher = Aes256Ecb::new_from_slices(&key, &iv)?;
    let mut buf = base64::decode(data)?;
    let decrypted_ciphertext = cipher.decrypt(&mut buf)?;
    let res=std::str::from_utf8(decrypted_ciphertext)?;
    Ok(res.to_string())
}


/** 解密 **/ 
pub fn gcm_decrypt(data: String,key_str: String,iv_str: String,add_str: String) ->  Result<String, Box<dyn Error>> {  
    let key = Key::from_slice(key_str.as_bytes());
    let cipher = Aes256Gcm::new(key);
    let nonce = Nonce::from_slice(iv_str.as_bytes());
    let payload = Payload {
        msg: data.as_bytes(),
        aad: add_str.as_bytes(),
    };
    let decrypt_data = cipher.decrypt(nonce, payload);
    if let Err(e) = decrypt_data {
        return Err(Box::<dyn Error + Send + Sync>::from(e.to_string()));
    }
    let result = String::from_utf8(decrypt_data.unwrap());
    match result {
        Ok(v_result) => Ok(v_result),
        Err(e) => {
            Err(Box::new(e))
        }
    }
}



/** 加密 **/ 
pub fn gcm_encrypt(data: String,key_str: String,iv_str: String,add_str: String) ->  Result<String, Box<dyn Error>>{ 
    let key = Key::from_slice(key_str.as_bytes());
    let cipher = Aes256Gcm::new(key);
    let nonce = Nonce::from_slice(iv_str.as_bytes());
    let payload = Payload {
        msg: data.as_bytes(),
        aad: add_str.as_bytes(),
    };
    let encrypt_data = cipher.encrypt(nonce, payload);
    if let Err(e) = encrypt_data {
        return Err(Box::<dyn Error + Send + Sync>::from(e.to_string()));
    }
    let ciphertext = encrypt_data.unwrap();  
    //let (ct, tag) = ciphertext.split_at(ciphertext.len() - 16);
    let result = String::from_utf8(ciphertext);
    match result {
        Ok(v_result) => Ok(v_result),
        Err(e) => {
            Err(Box::new(e))
        }
    }
}

#[cfg(test)]
mod aes_256_tests {
    use crate::aes_helper;
    #[test]
    fn aes_256_ecb_encrypt_test() {
        todo!()
    }

    #[test]
    fn aes_256_ecb_decrypt_test() {
        todo!()
    } 
}